/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.cba.solgar.game.rogal.generators;

import java.util.Random;
import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import pl.cba.solgar.game.rogal.Rogal;
import pl.cba.solgar.game.rogal.map.Map;
import pl.cba.solgar.game.rogal.map.MapTile;
import pl.cba.solgar.game.rogal.map.tiles.AbstractTile;

/**
 *
 * @author User
 */
public class BSPGenerator implements Generator
{

    private Map map;
    private Random random;
    private Room root;
    int digged;
    public static boolean dig_rooms;

    public BSPGenerator(Map map, long seed, boolean rooms)
    {
        dig_rooms = rooms;
        this.map = map;
        random = new Random(seed);
    }

    public void connect()
    {
        root.connectChilds();
    }

    public void dig()
    {
        digged = 0;
        MapTile[][] mapData = map.getMapData();
        for (int i = 0; i < mapData.length; ++i)
        {
            for (int j = 0; j < mapData[0].length; ++j)
            {
                mapData[i][j].tile = Map.tileDefinitions.get("cave_wall");
            }
        }
        root.dig(dig_rooms);
        System.out.println("digged: " + digged);
    }

    public void step()
    {
        digged = 0;
        MapTile[][] mapData = map.getMapData();
        for (int i = 0; i < mapData.length; ++i)
        {
            for (int j = 0; j < mapData[0].length; ++j)
            {
                mapData[i][j].tile = Map.tileDefinitions.get("cave_wall");
            }
        }
        root.createChilds(random);
        root.dig(dig_rooms);
    }

    public void generate(Map map)
    {
        digged = 0;
        // prepare Map
        MapTile[][] mapData = map.getMapData();

        for (int i = 0; i < mapData.length; ++i)
            for (int j = 0; j < mapData[0].length; ++j)
                mapData[i][j].tile = Map.tileDefinitions.get("cave_wall");

        root = new Room(mapData);
        root.x = 0;
        root.y = 0;
        root.w = mapData.length;
        root.h = mapData[0].length;
        root.createChilds(random);
        root.dig(dig_rooms);
    }

    public void postProcess()
    {
        replace(Map.tileDefinitions.get("cave_wall"),
                Map.tileDefinitions.get("cave_floor"),
                5,
                true);
        replace(Map.tileDefinitions.get("cave_wall"),
                Map.tileDefinitions.get("cave_floor"),
                6,
                true);

        replace(Map.tileDefinitions.get("cave_floor"),
                Map.tileDefinitions.get("cave_wall"),
                6,
                true);
    }

    public void paintDebug(Graphics g)
    {
        root.paintDebug(g);
    }

    private void replace(AbstractTile what, AbstractTile with, int value, boolean greater_than)
    {
        MapTile[][] mapData = map.getMapData();
        for (int i = 0; i < mapData.length; ++i)
        {
            for (int j = 0; j < mapData[0].length; ++j)
            {
                if (mapData[i][j].tile == what)
                {
                    int count = 0;
                    if ((j == 0 && i == 0) || (j == mapData[0].length - 1 && i == mapData.length))
                        count += 5;
                    else
                        if (i == 0 || j == 0)
                        {
                            count += 3;
                        }
                    for (int a = 0; a < 3; ++a)
                    {
                        for (int b = 0; b < 3; ++b)
                        {
                            try
                            {
                                if (mapData[i + a - 1][j + b - 1].tile == with)
                                    count += 1;
                            }
                            catch (Exception e)
                            {
                            }
                        }
                    }
                    if (greater_than)
                    {
                        if (count > value)
                            mapData[i][j].tile = with;
                    }
                    else
                    {
                        if (count < value)
                            mapData[i][j].tile = with;
                    }
                }
            }
        }
    }

    public class Room
    {

        int x;
        int y;
        int w;
        int h;
        int wall_x;
        int wall_y;
        int wall_w;
        int wall_h;
        Room[] children;
        MapTile[][] mapData;

        public void paintDebug(Graphics g)
        {
            if (children[0] != null)
                children[0].paintDebug(g);

            if (children[1] != null)
                children[1].paintDebug(g);

            if (children[0] == null && children[1] == null)
            {
                g.setColor(Color.red);
                g.drawRect(x * Rogal.FONT_WIDTH, (y+Rogal.MESSAGE_CONSOLE_SIZE) * Rogal.FONT_HEIGHT, w * Rogal.FONT_WIDTH, h * Rogal.FONT_HEIGHT);
            }
        }

        public Room(MapTile[][] mapData)
        {
            children = new Room[2];
            this.mapData = mapData;
        }

        public void connectChilds()
        {
            if (children[0] != null)
                children[0].connectChilds();

            if (children[1] != null)
                children[1].connectChilds();

            if (children[0] != null && children[1] != null)
            {
                int cx1 = children[0].x + children[0].w / 2;
                int cx2 = children[1].x + children[1].w / 2;
                int cy1 = children[0].y + children[0].h / 2;
                int cy2 = children[1].y + children[1].h / 2;
                int x_offset = cx1 - cx2;
                x_offset *= x_offset < 0 ? -1 : 1;
                int y_offset = cy1 - cy2;
                y_offset *= y_offset < 0 ? -1 : 1;
                if (x_offset < y_offset)
                {
                    int initial = cy1 < cy2 ? cy1 : cy2;
                    int last = cy1 < cy2 ? cy2 : cy1;
                    for (int i = initial; i < last; ++i)
                        map.getMapData()[(cx1 + cx2) / 2][i].tile = Map.tileDefinitions.get("cave_floor");
                }
                else
                {
                    int initial = cx1 < cx2 ? cx1 : cx2;
                    int last = cx1 < cx2 ? cx2 : cx1;
                    for (int i = initial; i < last; ++i)
                        map.getMapData()[i][(cy1 + cy2) / 2].tile = Map.tileDefinitions.get("cave_floor");
                }
            }
        }

        private void digRooms()
        {
            float p = 0;
            int start_x = 0;
            int stop_x = 0;
            int start_y = 0;
            int stop_y = 0;

            int iteration = 0;

            do
            {
                p = (((float) random.nextInt(50)) / 100f) + 0.5f;

                if (iteration > 200)
                    p = 1.0f;

                float _w = p * w;
                float _h = p * h;
                int offset_x = w - (int) _w;
                int offset_y = h - (int) _h;

                if (offset_x > 0)
                    offset_x = random.nextInt(offset_x);

                if (offset_y > 0)
                    offset_y = random.nextInt(offset_y);

                start_x = x + offset_x + 1;
                stop_x = x + (int) _w - 1;
                start_y = y + offset_y + 1;
                stop_y = y + (int) _h - 1;
                iteration += 1;
            }
            while ((stop_x - start_x) < 4 || (stop_y - start_y) < 4);

            for (int i = start_x; i <= stop_x; ++i)
                for (int j = start_y; j <= stop_y; ++j)
                    mapData[i][j].tile = Map.tileDefinitions.get("cave_floor");
        }

        private final float[] cave_percentage =
        {
            1.0f, 0.75f, 0.45f, 0.1f, 0.1f, 0.05f
        };

        private void digCave()
        {
            for (int _x = x; _x < x + w; ++_x)
            {
                for (int _y = y; _y < y + h; ++_y)
                {
                    int x1 = _x - x;
                    int x2 = x + w - _x;
                    int y1 = _y - y;
                    int y2 = y + h - _y;
                    int offset_x = x1 < x2 ? x1 : x2;
                    int offset_y = y1 < y2 ? y1 : y2;
                    int border_offset = offset_x < offset_y ? offset_x : offset_y;
                    float chance = random.nextFloat();
                    float percentage = border_offset >= cave_percentage.length ? 0 : cave_percentage[border_offset];
                    mapData[_x][_y].tile = Map.tileDefinitions.get("cave_floor");
                    if (chance < percentage)
                        mapData[_x][_y].tile = Map.tileDefinitions.get("cave_wall");
                }
            }
        }

        public void dig(boolean rooms)
        {
            digged += 1;
            if (children[0] != null)
                children[0].dig(rooms);

            if (children[1] != null)
                children[1].dig(rooms);

            if (children[0] == null && children[1] == null)
            {
                if (rooms)
                    digRooms();
                else
                    digCave();
            }
        }

        public void createChilds(Random r)
        {
            if (children[0] != null || children[1] != null)
            {
                if (children[0] != null)
                {
                    children[0].createChilds(r);
                }
                if (children[1] != null)
                {
                    children[1].createChilds(r);
                }
                return;
            }
            final int div_const = 30;
            boolean horizontal_split = r.nextBoolean();
            if (horizontal_split)
            {
                float div_factor = ((float) r.nextInt(div_const)) / 100.f;
                boolean sign = r.nextBoolean();

                if (sign)
                {
                    div_factor = 0.5f + div_factor;
                }
                else
                {
                    div_factor = 0.5f - div_factor;
                }

                int width = (int) (div_factor * w);
                if ((width <= 5) || (w - width <= 5))
                {
                    System.out.println("dupa 1");
                    return;
                }
                children[0] = new Room(mapData);
                children[0].x = x;
                children[0].y = y;
                children[0].w = width;
                children[0].h = h;

                children[1] = new Room(mapData);
                children[1].x = x + width;
                children[1].y = y;
                children[1].w = w - width;
                children[1].h = h;
            }
            else
            {
                float div_factor = ((float) r.nextInt(div_const)) / 100.f;
                boolean sign = r.nextBoolean();

                if (sign)
                    div_factor = 0.5f + div_factor;
                else
                    div_factor = 0.5f - div_factor;

                int height = (int) (div_factor * h);
                if ((height <= 5) || (h - height <= 5))
                {
                    System.out.println("dupa 2");
                    return;
                }
                children[0] = new Room(mapData);
                children[0].x = x;
                children[0].y = y;
                children[0].w = w;
                children[0].h = height;

                children[1] = new Room(mapData);
                children[1].x = x;
                children[1].y = y + height;
                children[1].w = w;
                children[1].h = h - height;
            }
        }
    }
}
